package com.example.greedysnake.datas;

import android.app.Application;
import android.content.Context;
import android.content.SharedPreferences;
import android.graphics.Color;
import androidx.annotation.NonNull;
import androidx.lifecycle.AndroidViewModel;
import androidx.lifecycle.MutableLiveData;

import com.example.greedysnake.enums.GamingLevel;
import com.example.greedysnake.enums.GamingStatus;
import com.example.greedysnake.enums.MovePosition;

import java.util.ArrayDeque;
import java.util.Queue;
import java.util.Random;

public class SnakeGamingData extends AndroidViewModel {

    private static final String TAG = SnakeGamingData.class.getSimpleName();

    // snake start postion
    int startPositionX = 10;
    int startPositionY = 10;
    // food position
    int foodX = 5;
    int foodY = 5;
    // full snake position
    ArrayDeque<SnakeRectPosition> snakeRectPositionQueue;
    // current move position
    MovePosition movePosition = MovePosition.RIGHT;

    // rect width
    int rectWidth;

    // gaming status
    private final MutableLiveData<GamingStatus> gamingStatus = new MutableLiveData<>();

    // score
    private int score = 0;
    public static final String KEY_SHP_FILE_NAME = "snake";
    public static final String KEY_SHP_SCORE = "score";

    // level
    // int gamingLevel = GamingLevel.NORMAL.getMoveSpeed();
    public static final String KEY_SHP_LEVEL = "level";

    public SnakeGamingData(@NonNull @org.jetbrains.annotations.NotNull Application application) {
        super(application);
        gamingStatus.setValue(GamingStatus.GOING);
    }

    public void init(int rectWidth){
        // this.gamingLevel = getLevel();
        this.rectWidth = rectWidth;
        score = 0;
        snakeRectPositionQueue = new ArrayDeque<>();
        initGamingRectPositionData(startPositionX, startPositionY);
    }

    public void reInit() {
        init(this.rectWidth);
    }

    public SnakeRectPosition[][] getGamingRectPositionData(int rectWidth, int x , int y){

        SnakeRectPosition[][] snakeRectPositionArray = new SnakeRectPosition[y][x];
        SnakeRectPosition snakeRectPosition;

        for (int i = 0; i < y; i++){// 行 22
            for (int j = 0; j < x; j++){// 列 16
                snakeRectPosition = new SnakeRectPosition((j * rectWidth) + 2, (i * rectWidth) + 2, ((j + 1) * rectWidth) - 2, ((i + 1) * rectWidth) - 2, false);
                snakeRectPosition.setX(i);
                snakeRectPosition.setY(j);
                snakeRectPositionArray[i][j] = snakeRectPosition;
            }
        }

        return snakeRectPositionArray;
    }

    public void initGamingRectPositionData(int startPositionX, int startPositionY){
        snakeRectPositionQueue.add(new SnakeRectPosition(startPositionX, startPositionY));
    }

    public SnakeRectPosition[][] updateCurrentGamingData(Queue<SnakeRectPosition> snakeRectPositionQueue,
                                                         MovePosition movePosition,
                                                         int foodX, int foodY){

        SnakeRectPosition[][] data = getGamingRectPositionData(rectWidth, 16, 22);
        // food
        data[foodX][foodY].setColor(Color.BLUE);
        data[foodX][foodY].setFill(true);

        for (SnakeRectPosition snakeRectPosition : snakeRectPositionQueue){
            data[snakeRectPosition.getX()][snakeRectPosition.getY()].setColor(Color.RED);
            data[snakeRectPosition.getX()][snakeRectPosition.getY()].setFill(true);
            data[snakeRectPosition.getX()][snakeRectPosition.getY()].setMovePosition(movePosition);
        }

        return data;
    }

    public SnakeRectPosition[][] updateCurrentGamingData(){

        SnakeRectPosition[][] data = getGamingRectPositionData(rectWidth, 16, 22);
        // food
        data[foodX][foodY].setColor(Color.BLUE);
        data[foodX][foodY].setFill(true);

        for (SnakeRectPosition snakeRectPosition : snakeRectPositionQueue){
            data[snakeRectPosition.getX()][snakeRectPosition.getY()].setColor(Color.RED);
            data[snakeRectPosition.getX()][snakeRectPosition.getY()].setFill(true);
            data[snakeRectPosition.getX()][snakeRectPosition.getY()].setMovePosition(movePosition);
        }

        return data;
    }

    public boolean autoMove(){

        int nextX;
        int nextY;

        assert snakeRectPositionQueue.peek() != null;
        SnakeRectPosition peek = new SnakeRectPosition(snakeRectPositionQueue.peek());

        nextX = peek.getX();
        nextY = peek.getY();

        switch (movePosition){
            case LEFT: {
                nextY = peek.getY() - 1;
            }break;
            case TOP: {
                nextX = peek.getX() - 1;
            }break;
            case RIGHT: {
                nextY = peek.getY() + 1;
            }break;
            case BOTTOM: {
                nextX = peek.getX() + 1;
            }break;
            default:
        }

        if (judgeFail(nextX, nextY)){
            return false;
        }

        peek.setX(nextX);
        peek.setY(nextY);

        if (!(nextX == foodX && nextY == foodY)){
            snakeRectPositionQueue.removeLast();
        }else {
            score++;
            generateNewFood();
        }
        snakeRectPositionQueue.offerFirst(peek);
        return true;
    }

    public void setMovePosition(MovePosition position){
        movePosition = position;
    }

    public boolean judgeFail(int nextX, int nextY){
        // 边界
        if(nextX < 0 || nextY < 0 || nextX > 21 || nextY > 15){
            return true;
        }
        // 碰到自己
        for (SnakeRectPosition snakeRectPosition : snakeRectPositionQueue){
            if (nextX == snakeRectPosition.getX() && nextY == snakeRectPosition.getY()) {
                return true;
            }
        }
        return false;
    }

    public void generateNewFood(){
        foodX = new Random().nextInt(21);
        foodY = new Random().nextInt(15);
    }

    public MutableLiveData<GamingStatus> getGamingStatus() {
        return gamingStatus;
    }

    public void saveScore(){
        SharedPreferences sharedPreferences = getApplication().getSharedPreferences(KEY_SHP_FILE_NAME, Context.MODE_PRIVATE);
        SharedPreferences.Editor edit = sharedPreferences.edit();
        if (score > getScore()){
            edit.putInt(KEY_SHP_SCORE, score);
            edit.apply();
        }
    }

    public int getScore(){
        SharedPreferences sharedPreferences = getApplication().getSharedPreferences(KEY_SHP_FILE_NAME, Context.MODE_PRIVATE);
        return sharedPreferences.getInt(KEY_SHP_SCORE, 0);
    }

    public int getLevel(){
        SharedPreferences sharedPreferences = getApplication().getSharedPreferences(KEY_SHP_FILE_NAME, Context.MODE_PRIVATE);
        return sharedPreferences.getInt(KEY_SHP_LEVEL, GamingLevel.NORMAL.getMoveSpeed());
    }

}
